
#include <ctype.h>

#include "Parser.h"
#include "ParserException.h"
#include "InterpretPrescription.h"
#include "util.h"
#include <sstream>
#include <stdlib.h>
#include <vector>
#include "Class.h"
bool isop(int c){
    return ispunct(c)&&c!='.'&&c!=']'&&c!=')'&& c!='_' && c!='(' && c!='[' && c!=':';
}
bool isid(int c){
    return isalpha(c);
}
bool isidpart(int c){
    return isid(c)||c=='_';
}
bool isterm(int c){
    return c==']'||c==')'||c=='.';
}
Parser::Parser(Image* image){
    Parser::image=image;
}
Expression* Parser::parseExpr(istream& stream){
    Expression* receiver=parseOneExpr(stream);
    skipWhitespace(stream);
    if(stream.eof() || isterm(stream.peek())){
        return receiver;
    }
    ostringstream selectors;
    vector<Expression*> arguments;
    while(! (stream.eof() || isterm(stream.peek())) ){
        bool isOperator=isop(stream.peek());
        string selector=parseId(stream);
        selectors << selector;
        if((stream.peek()!=':' && !isOperator)|| isterm(stream.peek())) break;
        if(stream.peek()==':') stream.get();
        selectors << ":";
        Expression* argument=parseOneExpr(stream);
        arguments.push_back(argument);
        skipWhitespace(stream);
    }
    Expression* e=new Expression();
    MessageSend* ms=new MessageSend();
    e->type=EXPRESSION_MESSAGE;
    e->message=ms;
    ms->object=receiver;
    ms->selector=selectors.str();
    ms->arguments=arguments;
    return e;
}
Expression* Parser::parseOneExpr(istream& stream){
    skipWhitespace(stream);
    if(isdigit(stream.peek())){
        string id=parseId(stream);
        Expression* e=new Expression();
        e->type=EXPRESSION_CONST;
        e->constant=makeInteger(image,atoi(id.c_str()));
        return e;
    }else if(stream.peek()=='"'){
        Expression* e=new Expression();
        e->type=EXPRESSION_CONST;
        stream.get();
        ostringstream sstream;
        while(stream.peek()!='"'){
            sstream.put(stream.get());
        }
        stream.get();
        e->constant=makeString(image,(char*)sstream.str().c_str());
        return e;
    }else if(stream.peek()=='('){
        stream.get();
        Expression* e=parseExpr(stream);
        skipWhitespace(stream);
        if(stream.get()!=')'){
            throw new ParserException();
        }
        return e;
    }else if(stream.peek()=='['){
        stream.get();
        Expression* e=new Expression();
        e->type=EXPRESSION_CLOSURE;
        InterpretPrescription* ip=new InterpretPrescription();
        e->cp=ip;
        skipWhitespace(stream);
        while(stream.peek()!='|' && !stream.eof()){
            string argument=parseId(stream);
            ip->addArgument(NULL,argument);
            if(stream.peek()!=':'){
                throw new ParserException();
            }
            stream.get();
            skipWhitespace(stream);
        }
        stream.get();
        skipWhitespace(stream);
        while(stream.peek()!='|' && !stream.eof()){
            string temp=parseId(stream);
            ip->addTemp(NULL,temp);
            skipWhitespace(stream);
        }
        stream.get();
        skipWhitespace(stream);
        while(stream.peek()!=']'&& !stream.eof()){
            Statement st=parseStatement(stream);
            ip->add(st);
            skipWhitespace(stream);
            if(stream.peek()!='.'&&stream.peek()!=']'){
                throw new ParserException();
            }
            if(stream.peek()=='.') stream.get();
            skipWhitespace(stream);
        }
        if(stream.get()!=']') throw new ParserException();
        return e;
    }else if(isid(stream.peek())){
        string id=parseId(stream);
        Expression* e=new Expression();
        if(id=="self"){
            e->type=EXPRESSION_SELF;
        }else if(id=="true" || id=="false"){
            e->type=EXPRESSION_CONST;
            e->constant=makeBoolean(image,id=="true"?true:false);
        }else{
            e->type=EXPRESSION_VAR;
            e->var=id;
        }
        return e;
    }else{
        printf("Not exp %c\n",stream.peek());
        throw new ParserException();
    }
}
Statement Parser::parseStatement(istream& stream){
    int beginning=stream.tellg();
    Statement st;
    skipWhitespace(stream);
    if(stream.peek()=='^'){
        stream.get();
        st.rvalue=parseExpr(stream);
        st.type=STATEMENT_RETURN;
    }else{
        string lvalue;
        try {
            lvalue=parseId(stream);
        }catch(ParserException* e){
            lvalue="";
        }
        string op;
        try{
            op=parseId(stream);
        }catch(ParserException* e){
            op="";
        }
        if(lvalue=="" || op!="<-"){
            stream.seekg(beginning);
            stream.clear();
            st.type=STATEMENT_DISCARD;
            st.rvalue=parseExpr(stream);
        }else{
            st.type=STATEMENT_ASSIGN;
            st.rvalue=parseExpr(stream);
            st.lvalue=lvalue;
        }
    }
    return st;
}
void Parser::skipStatementRemnants(istream& stream){
    skipWhitespace(stream);
    if(stream.peek()!='.') throw new ParserException();
    stream.get();
    skipWhitespace(stream);
}
CodeMethod* Parser::parseMethod(istream& stream){
    ostringstream selector;
    selector<<parseId(stream);
    //printf("Selector %s.\n",selector.str().c_str());
    vector<string> args;
    skipWhitespace(stream);
    if(stream.peek()=='['){
        stream.get();
    }else if(stream.peek()==':'){
        selector<<":";
        stream.get();
        string arg=parseId(stream);
        //printf("Arg %s.\n",arg.c_str());
        args.push_back(arg);
        skipWhitespace(stream);
        while(stream.peek()!='['){
            selector<<parseId(stream);
            selector<<":";
            //printf("Selector %s.\n",selector.str().c_str());
            skipWhitespace(stream);
            if(stream.peek()!=':'){
                printf("%c not exp.\n",stream.peek());
                throw new ParserException();
            }
            stream.get();
            //printf("Arg %s.\n",arg.c_str());
            arg=parseId(stream);
            args.push_back(arg);
            skipWhitespace(stream);
        }
        stream.get();
    }else{
        printf("Inexpected %c\n",stream.peek());
        throw new ParserException();
    }
    skipWhitespace(stream);
    //create the method itself
    CodeMethod* cm=new CodeMethod(image,selector.str());
    for(int i=0;i<args.size();i++){
        cm->addArgument(NULL,args[i]);
    }
    while(stream.peek()!=']'){
        Statement st=parseStatement(stream);
        cm->add(st);
        skipWhitespace(stream);
        if(stream.peek()=='.'){
            stream.get();
            skipWhitespace(stream);
        }
    }
    stream.get();
    return cm;
}
string Parser::parseId(istream& stream){
    skipWhitespace(stream);
    int firstChar=stream.get();
    if(firstChar=='('||firstChar==')'||firstChar=='['||firstChar==']'){
        throw new ParserException();
    }
    ostringstream sstream;
    sstream.put(firstChar);
    if(isdigit(firstChar)){
        while(isdigit(stream.peek())){
            sstream.put(stream.get());
        }
    }else if(isop(firstChar)){
        while(isop(stream.peek())){
            sstream.put(stream.get());
        }
    }else if(isid(firstChar)){
        while(isidpart(stream.peek())){
            sstream.put(stream.get());
        }
    }
    return sstream.str();
}
void Parser::skipWhitespace(istream& stream){
    while(isspace(stream.peek())){
        stream.get();
    }
}